#include "udstp.h"
#include <cstring>

uint32_t UdsTp::xRxId;
uint32_t UdsTp::xTxPhyId;
uint32_t UdsTp::xTxFunId;
uint8_t UdsTp::gSTmin;
tCanTpWorkStatus UdsTp::gs_eCanTpWorkStatus;
tCanTpInfo UdsTp::gs_stCanTPTxDataInfo;
tCanTpInfo UdsTp::gs_stCanTPRxDataInfo;

UdsTp::UdsTp()
{
    disconnect(&TpTimer, &QTimer::timeout, this, &UdsTp::Tick1ms);
    connect(&TpTimer, &QTimer::timeout, this, &UdsTp::Tick1ms);
    TpTimer.setTimerType(Qt::PreciseTimer);
    TpTimer.start(1);
}

void UdsTp::Tick1ms()
{
    if(gSTmin > 0)
    {
        gSTmin--;
    }
}

void UdsTp::run()
{
    sCANObj sMsg = {0};

    while(bRunFlag == true)
    {
        if(gs_eCanTpWorkStatus == IDLE)
        {
            QThread::sleep(0);
        }
        else if(gs_eCanTpWorkStatus == WAITTING_TX)
        {
            sMsg.ID = gs_stCanTPTxDataInfo.xCanTpId;
            sMsg.uLen = 8;
            if(gs_stCanTPTxDataInfo.xPduDataLen < 8)
            {
                sMsg.Data[0] = (SF << 4) | gs_stCanTPTxDataInfo.xPduDataLen;
                memcpy(&sMsg.Data[1], gs_stCanTPTxDataInfo.aDataBuf, gs_stCanTPTxDataInfo.xPduDataLen);
                gs_stCanTPTxDataInfo.xPduDoneLen += gs_stCanTPTxDataInfo.xPduDataLen;
                emit updateTxMsg(sMsg);
                gs_stCanTPTxDataInfo.xPduDataLen = 0;
                gs_stCanTPTxDataInfo.xPduDoneLen = 0;
                gs_stCanTPTxDataInfo.isNeedTx = false;
                gs_eCanTpWorkStatus = IDLE;
            }
            else
            {
                sMsg.Data[0] = (FF << 4) | ((gs_stCanTPTxDataInfo.xPduDataLen >> 8) & 0x0f);
                sMsg.Data[1] = gs_stCanTPTxDataInfo.xPduDataLen & 0xff;
                memcpy(&sMsg.Data[2], gs_stCanTPTxDataInfo.aDataBuf, 6);
                gs_stCanTPTxDataInfo.ucSN = 1;
                gs_stCanTPTxDataInfo.xPduDoneLen = 6;
                emit updateTxMsg(sMsg);
                gs_eCanTpWorkStatus = RX_FC;
            }
        }
        else if(gs_eCanTpWorkStatus == TX_FC)
        {
            sMsg.ID = GetConfigTxPhyMsgID();
            sMsg.uLen = 8;
            sMsg.Data[0] = FC << 4;
            sMsg.Data[1] = BSize;
            sMsg.Data[2] = STmin;
            emit updateTxMsg(sMsg);
            gs_eCanTpWorkStatus = RX_CF;
        }
        else if(gs_eCanTpWorkStatus == TX_CF)
        {
            sMsg.ID = GetConfigTxPhyMsgID();
            sMsg.uLen = 8;
            if(gs_stCanTPTxDataInfo.xSTmin == 0)
            {
                sMsg.Data[0] = (CF << 4) | (gs_stCanTPTxDataInfo.ucSN & 0x0F);
                memcpy(&sMsg.Data[1], &gs_stCanTPTxDataInfo.aDataBuf[gs_stCanTPTxDataInfo.xPduDoneLen], 7);
                gs_stCanTPTxDataInfo.xPduDoneLen += 7;
                gs_stCanTPTxDataInfo.ucSN++;
                if(gs_stCanTPTxDataInfo.xPduDoneLen >= gs_stCanTPTxDataInfo.xPduDataLen)
                {
                    gs_stCanTPTxDataInfo.isNeedTx = false;
                    gs_eCanTpWorkStatus = IDLE;
                }
                emit updateTxMsg(sMsg);
            }
            else
            {
                if(gSTmin == 0 || gs_stCanTPTxDataInfo.ucSN == 1)
                {
                    gSTmin = gs_stCanTPTxDataInfo.xSTmin;
                    sMsg.Data[0] = (CF << 4) | gs_stCanTPTxDataInfo.ucSN;
                    memcpy(&sMsg.Data[1], &gs_stCanTPTxDataInfo.aDataBuf[gs_stCanTPTxDataInfo.xPduDoneLen], 7);
                    gs_stCanTPTxDataInfo.xPduDoneLen += 7;
                    gs_stCanTPTxDataInfo.ucSN++;
                    if(gs_stCanTPTxDataInfo.xPduDoneLen >= gs_stCanTPTxDataInfo.xPduDataLen)
                    {
                        gs_stCanTPTxDataInfo.isNeedTx = false;
                        gs_eCanTpWorkStatus = IDLE;
                    }
                    emit updateTxMsg(sMsg);
                }
            }
        }
    }
}

void UdsTp::Initialization()
{
    xRxId = jsonTpInfor.value("RxId").toString().toUShort(nullptr, 16);
    xTxPhyId = jsonTpInfor.value("TxPhyId").toString().toUShort(nullptr, 16);
    xTxFunId = jsonTpInfor.value("TxFunId").toString().toUShort(nullptr, 16);
    eCanType = CANTP_STANDARD;
    gs_eCanTpWorkStatus = IDLE;
    memset(&gs_stCanTPTxDataInfo, 0u, sizeof(gs_stCanTPTxDataInfo));
    memset(&gs_stCanTPRxDataInfo, 0u, sizeof(gs_stCanTPRxDataInfo));
}

void UdsTp::DriverWriteAFrameInTP(const uint32_t i_RxID, const uint16_t i_RxDataLen, const uint8_t *i_pRxDataBuf)
{
    if(true == IsReceivedMsgIDValid(i_RxID))
    {
        if(i_RxDataLen == 8)
        {
            if(IsSF(i_pRxDataBuf[0]))
            {
                gs_stCanTPRxDataInfo.xCanTpId = i_RxID;
                gs_stCanTPRxDataInfo.xPduDataLen = i_pRxDataBuf[0];
                memcpy(gs_stCanTPRxDataInfo.aDataBuf, &i_pRxDataBuf[1], gs_stCanTPRxDataInfo.xPduDataLen);
                gs_stCanTPRxDataInfo.isFinish = true;
                gs_eCanTpWorkStatus = IDLE;
            }
            else if(IsFF(i_pRxDataBuf[0]))
            {
                gs_stCanTPRxDataInfo.xCanTpId = i_RxID;
                gs_stCanTPRxDataInfo.xPduDataLen = ((i_pRxDataBuf[0] & 0x0F) << 4 )+ i_pRxDataBuf[1];
                memcpy(gs_stCanTPRxDataInfo.aDataBuf, &i_pRxDataBuf[2], 6);
                gs_stCanTPRxDataInfo.xPduDoneLen = 6;
                gs_stCanTPRxDataInfo.ucSN = 1;
                gs_eCanTpWorkStatus = TX_FC;
            }
            else if(IsCF(i_pRxDataBuf[0]))
            {
                if(gs_eCanTpWorkStatus == RX_CF)
                {
                    if(gs_stCanTPRxDataInfo.ucSN == (i_pRxDataBuf[0] & 0x0F))
                    {
                        gs_stCanTPRxDataInfo.ucSN++;
                        memcpy(&gs_stCanTPRxDataInfo.aDataBuf[gs_stCanTPRxDataInfo.xPduDoneLen], &i_pRxDataBuf[1], 7);
                        gs_stCanTPRxDataInfo.xPduDoneLen += 7;
                        if(gs_stCanTPRxDataInfo.xPduDoneLen >= gs_stCanTPRxDataInfo.xPduDataLen)
                        {
                            gs_stCanTPRxDataInfo.isFinish = true;
                            gs_eCanTpWorkStatus = IDLE;
                        }
                    }
                }
            }
            else if(IsFC(i_pRxDataBuf[0]))
            {
                if(gs_eCanTpWorkStatus == RX_FC)
                {
                    gs_stCanTPTxDataInfo.xSTmin = i_pRxDataBuf[2];
                    gSTmin = gs_stCanTPTxDataInfo.xSTmin;
                    gs_eCanTpWorkStatus = TX_CF;
                }
            }
        }
    }
}

bool UdsTp::DriverReadRespFormTP(uint32_t *i_RxID, uint16_t *i_RxDataLen, uint8_t *i_pRxDataBuf)
{
    bool result = false;

    if(gs_eCanTpWorkStatus == IDLE)
    {
        if(gs_stCanTPRxDataInfo.isFinish == true)
        {
            *i_RxID = gs_stCanTPRxDataInfo.xCanTpId;
            *i_RxDataLen = gs_stCanTPRxDataInfo.xPduDataLen;
            memcpy(i_pRxDataBuf, gs_stCanTPRxDataInfo.aDataBuf, gs_stCanTPRxDataInfo.xPduDataLen);
            result = true;
        }
    }

    return result;
}

void UdsTp::DriverSetTxDataInTP(const uint32_t i_RxID, const uint16_t i_TxDataLen, const uint8_t *i_pTxDataBuf, const uint8_t *i_pExpectBuf)
{
    (void)i_pExpectBuf;
    gs_stCanTPTxDataInfo.isFinish = false;
    gs_stCanTPTxDataInfo.xCanTpId = i_RxID;
    gs_stCanTPTxDataInfo.xPduDataLen = i_TxDataLen;
    gs_stCanTPTxDataInfo.xPduDoneLen = 0;
    memcpy(gs_stCanTPTxDataInfo.aDataBuf, i_pTxDataBuf, i_TxDataLen);
    gs_stCanTPTxDataInfo.isNeedTx = true;
    gs_eCanTpWorkStatus = WAITTING_TX;
    gs_stCanTPRxDataInfo.isFinish = false;
}

bool UdsTp::IsReceivedMsgIDValid(const uint32_t i_RxID)
{
    bool result = false;

    if(i_RxID == xRxId)
    {
        result = true;
    }

    return result;
}

uint32_t UdsTp::GetConfigTxPhyMsgID()
{
    return xTxPhyId;
}

uint32_t UdsTp::GetConfigTxFunMsgID()
{
    return xTxFunId;
}
